﻿/*
 * Copyright (c) HiSilicon (Shanghai) Technologies Co., Ltd. 2023-2023. All rights reserved.
 */

#include "Global.h"
#include <QFileDialog>
#include <QMessageBox>
#include <QCoreApplication>
#include <QSettings>
extern "C" {
#include <hidsdi.h>
};
#include <windows.h>
#include <cfgmgr32.h>
#include <initguid.h>
#include <usbioctl.h>
#include <QTextCodec>
#include <QSet>
#include <QDateTime>

namespace  {
    const int WAIT_SHORT_TIMEOUT = 2500;
    const int WAIT_COMMON_TIMEOUT  = 15000;
    const int WAIT_LONG_TIMEOUT = 30000;
    const int WAIT_COMMON_PRECISION = 100;
    const int COMPLITE_TIMEOUT = 200;
    const int RETRY_TIMEOUT = 500;
    const int TIME_VAL_TEN = 10;
    const int TIME_VAL_TWO = 2;
    const unsigned int NERR_BASE = 2100;
    const unsigned int MAX_NERR = 2100 + 899;
    const int MAX_CHIP_NUM = 100;
    const int LEN = 256;
    const int RET_TWO = 2;
}


BIN_INFO::BIN_INFO()
    : type(IMAGE_TYPE::IMAGE_TYPE_NORMAL)
{
    path.clear();
    fileName.clear();
    fileIndex.clear();
    burnAddr.clear();
    burnSize.clear();
    fileSize.clear();
}
int BIN_INFO::GetFileIndex() const
{
    return Str2U32DecHex(fileIndex);
}
int BIN_INFO::GetFileSize() const
{
    return Str2U32DecHex(fileSize);
}
int BIN_INFO::GetBurnAddr() const
{
    return Str2U32DecHex(burnAddr);
}
int BIN_INFO::GetBurnSize() const
{
    return Str2U32DecHex(burnSize);
}

int BIN_INFO::Str2U32DecHex(const QString &str) const
{
    QString mStrPrefix = str.left(2);
    int nBase = 0;
    if (mStrPrefix.compare("0x") == 0) { // Hex
        nBase = 16; // 若以0x开头为16进制
    } else {
        nBase = 10; // 若没有0x则为10进制
    }
    bool ok;
    U32 ulRet = str.toUInt(&ok, nBase);
    return ulRet;
}
unsigned char Global::shakeHandSignal[SHAKE_HAND_SIGNAL_LEN] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
unsigned char Global::constShakeHandSignal[SHAKE_HAND_SIGNAL_LEN] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                                                                     0, 0, 0, 0, 0, 0, 0};
unsigned int Global::chlStartFlag = 0;
ChipType Global::currentChipTypeId = ChipType::WIFI5GNB;
unsigned int Global::timerPlus = 2;
unsigned int Global::timerIdWaitInterruptFlagTimeout = WAIT_COMMON_TIMEOUT * timerPlus;
unsigned int Global::timerIdWaitCommonTimeout = WAIT_COMMON_TIMEOUT * timerPlus;
unsigned int Global::timerIdWaitLongTimeout = WAIT_LONG_TIMEOUT * timerPlus;
unsigned int Global::timerIdWaitShortTimeout = WAIT_SHORT_TIMEOUT * timerPlus;
unsigned int Global::timerIdWaitCommonPrecision = WAIT_COMMON_PRECISION;
unsigned int Global::timerIdCompliteTimeout = COMPLITE_TIMEOUT;
unsigned int Global::timerIdRetryTimeout = RETRY_TIMEOUT;
QMap <int, ChipTypeInfo> Global::allChipTypeInfo;
QMutex Global::mMutex;
QSet<QString> Global::mUsbSerialLists;
QString Global::dataTime = QDateTime::currentDateTime().toString("_yyyyMMddhhmmss");

QString Global::GetCurrentChipVer()
{
    return "";
}

QString Global::GetAppConfigPath()
{
    QString fileName = QCoreApplication::applicationDirPath();
    return fileName + "\\configure";
}

void Global::ReadCfg()
{
    QSettings cfg(GetAppConfigPath() + "\\config_chip_type.ini", QSettings::IniFormat);
    Global::currentChipTypeId = static_cast<ChipType>(cfg.value("ChipType/CurrentChipTypeId").toInt());
    if (Global::currentChipTypeId == static_cast<ChipType>(0)) {
        Global::currentChipTypeId = ChipType::SPARTA;
    }

    if (Global::currentChipTypeId == ChipType::WIFI5GNB ||
        Global::currentChipTypeId == ChipType::WIFIDW31 ||
        Global::currentChipTypeId == ChipType::SWAN ||
        Global::currentChipTypeId == ChipType::BS2X ||
        IsWs63(currentChipTypeId) ||
        Global::currentChipTypeId == ChipType::WS53 ||
        Global::currentChipTypeId == ChipType::WIFI4GCAT1 ||
        Global::currentChipTypeId == ChipType::SW39 ||
        Global::currentChipTypeId == ChipType::SW21 ||
        Global::currentChipTypeId == ChipType::HI3372 ||
        Global::currentChipTypeId == ChipType::HI3322 ||
        Global::currentChipTypeId == ChipType::COM2982) {
        unsigned char shakeHandSignal1[SHAKE_HAND_SIGNAL_LEN] = {0xEF, 0xBE, 0xAD, 0xDE, 0x12,
                                                                 0x00, 0xF0, 0x0F, 0x00, 0xC2,
                                                                 0x01, 0x00, 0x08, 0x01, 0x00,
                                                                 0x00, 0xe0, 0x64 };
        errno_t ret = memcpy_s(Global::shakeHandSignal, SHAKE_HAND_SIGNAL_LEN, shakeHandSignal1, SHAKE_HAND_SIGNAL_LEN);
        errno_t constRet = memcpy_s(Global::constShakeHandSignal, SHAKE_HAND_SIGNAL_LEN,
                                    shakeHandSignal1, SHAKE_HAND_SIGNAL_LEN);
        if (ret != 0 || constRet != 0) {
            return;
        }
        chlStartFlag = 0xDEADBEEF;
    } else {
        unsigned char shakeHandSignal1[SHAKE_HAND_SIGNAL_LEN] = {0xc8, 0xd7, 0xe6, 0xf5, 0x12,
                                                                 0x00, 0xF0, 0x0F, 0x00, 0xC2,
                                                                 0x01, 0x00, 0x08, 0x01, 0x00,
                                                                 0x00, 0x2f, 0xD0 };
        errno_t ret = memcpy_s(Global::shakeHandSignal, SHAKE_HAND_SIGNAL_LEN, shakeHandSignal1, SHAKE_HAND_SIGNAL_LEN);
        errno_t constRet = memcpy_s(Global::constShakeHandSignal, SHAKE_HAND_SIGNAL_LEN,
                                    shakeHandSignal1, SHAKE_HAND_SIGNAL_LEN);
        if (ret != 0 || constRet != 0) {
            return;
        }
        chlStartFlag = 0xf5e6d7c8;
    }
    ReadCfgSub();
}

void Global::ReadCfgSub()
{
    if (Global::currentChipTypeId == ChipType::WIFIURANUS) {
        timerPlus = TIME_VAL_TEN;
    } else {
        timerPlus = TIME_VAL_TWO;
    }
    Global::timerIdWaitInterruptFlagTimeout = WAIT_COMMON_TIMEOUT * timerPlus;
    Global::timerIdWaitCommonTimeout = WAIT_COMMON_TIMEOUT * timerPlus;
    Global::timerIdWaitLongTimeout = WAIT_LONG_TIMEOUT * timerPlus;
    Global::timerIdWaitShortTimeout = WAIT_SHORT_TIMEOUT * timerPlus;
    Global::timerIdWaitCommonPrecision = WAIT_COMMON_PRECISION;
    Global::timerIdCompliteTimeout = COMPLITE_TIMEOUT;
    Global::timerIdRetryTimeout = RETRY_TIMEOUT;
}

void Global::SaveCfg()
{
    QSettings cfg(GetAppConfigPath() + "\\config_chip_type.ini", QSettings::IniFormat);
    cfg.setValue("ChipType/CurrentChipTypeId", static_cast<int>(Global::currentChipTypeId));
}

QStringList Global::StringToList(const QString &listStr)
{
    QStringList list;
    int j = 0;
    while (true) {
        QString str = listStr.section(",", j, j);
        if (!str.isEmpty()) {
            list.append(str);
        } else {
            break;
        }
        j++;
    }
    return list;
}

QString Global::ListToString(const QStringList &list)
{
    QString str;
    for (int j = 0; j < list.count(); j++) {
        if (j != 0) {
            str.append(",");
        }
        str.append(list.at(j));
    }
    return str;
}

QString Global::ChipTypeIdToString(ChipType chipTypeId)
{
    for (auto it = allChipTypeInfo.begin(); it != allChipTypeInfo.end(); it++) {
        if (it.value().id == static_cast<uint32_t>(chipTypeId)) {
            return it.value().name;
        }
    }
    return "";
}

ChipType Global::ChipTypeStringToId(const QString &chipType)
{
    for (auto it = allChipTypeInfo.begin(); it != allChipTypeInfo.end(); it++) {
        if (it.value().name.toLower() == chipType.toLower()) {
            return static_cast<ChipType>(it.value().id);
        }
    }
    return ChipType::WIFI5GNB;
}

QString Global::GetAtReset(const ChipType &chipType)
{
    for (auto it = allChipTypeInfo.begin(); it != allChipTypeInfo.end(); it++) {
        if (it.value().id == static_cast<uint32_t>(chipType)) {
            return it.value().atReset;
        }
    }
    return "";
}

void Global::ReadAllChipType()
{
    allChipTypeInfo.clear();
    QSettings cfg(GetAppConfigPath() + "\\config_all_chip_type.ini", QSettings::IniFormat);
    uint32_t configIdNum = cfg.value("AHHCHIPTYPECONFIG/CONFIG_ID_NUM").toUInt();
    if (configIdNum > MAX_CHIP_NUM) {
        configIdNum = MAX_CHIP_NUM;
    }
    for (uint32_t i = 1; i <= configIdNum; i++) {
        ChipTypeInfo chipType;
        chipType.id = cfg.value("AHHCHIPTYPECONFIG/CONFIG_ID" + QString::number(i)).toUInt();
        if (chipType.id == 0) {
            continue;
        }
        chipType.name = cfg.value("AHHCHIPTYPECONFIG/CHIP_NAME" + QString::number(i)).toString();
        chipType.baudRate = cfg.value("AHHCHIPTYPECONFIG/BAUND_RATE" + QString::number(i)).toUInt();
        chipType.atReset = cfg.value("AHHCHIPTYPECONFIG/AT_RESET" + QString::number(i)).toString();
        allChipTypeInfo[i] = chipType;
    }
}

int Global::ReadFileData(const QString &filePath, unsigned char *&data, DWORD &fileSize)
{
    QString cleanPath = QDir::cleanPath(filePath);
    if (cleanPath.isEmpty()) {
        return -1;
    }
    QFile file(cleanPath);
    if (!file.open(QIODevice::ReadOnly)) {
        file.close();
        return -1;
    }
    fileSize = file.size() + 1;          // 得到文件的大小
    if (fileSize <= 0) {
        qDebug("Memory is not compliant");
    }
    data = new BYTE[fileSize]();
    if (data == nullptr) {
        return -1;
    }
    SecureZeroMemory(data, fileSize);
    // 读文件
    if (file.read(reinterpret_cast<char *>(data), fileSize) <= 0) {
        GetLastError();
        return -1;
    }
    file.close();
    return fileSize;
}

void Global::ShowData(const unsigned char* pData, unsigned long ulDataLen)
{
    Q_UNUSED(pData);
    Q_UNUSED(ulDataLen);
}

void Global::ActionFailed(DWORD dwLastError)
{
    HMODULE hModule = nullptr; // default to system source
    LPSTR messageBuffer = nullptr;
    DWORD dwBufferLength = 0;

    DWORD dwFormatFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER |
        FORMAT_MESSAGE_IGNORE_INSERTS |
        FORMAT_MESSAGE_FROM_SYSTEM;

    if (dwLastError >= NERR_BASE && dwLastError <= MAX_NERR) {
        hModule = LoadLibraryExA("netmsg.dll", nullptr, LOAD_LIBRARY_AS_DATAFILE);
        if (hModule != nullptr) {
            dwFormatFlags |= FORMAT_MESSAGE_FROM_HMODULE;
        }
    }

    if ((dwBufferLength = FormatMessageA(dwFormatFlags, hModule,
        dwLastError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        reinterpret_cast<LPSTR>(&messageBuffer), 0, nullptr)) != 0) {
        LocalFree(messageBuffer);
    }

    if (hModule != nullptr) {
        FreeLibrary(hModule);
    }
}

void Global::EndianSwap(BYTE* pData, DWORD length)
{
    int i;
    int cnt;
    int end;
    cnt = length / TIME_VAL_TWO;
    end = length - 1;
    BYTE tmp;
    for (i = 0; i < cnt; i++) {
        tmp = pData[i];
        pData[i] = pData[end - i];
        pData[end - i] = tmp;
    }
}

bool Global::IsUSBType(ChipType type)
{
    return (type == ChipType::USBBS25 || type == ChipType::USBBS2X || type == ChipType::USBBS27 ||
            type == ChipType::USB4GCAT1 || type == ChipType::USB2982 || type == ChipType::USBHI3322);
}

bool Global::IsHsanType(ChipType type)
{
    return (type == ChipType::HSANLUOFU || type == ChipType::HSANXILING ||
            type == ChipType::HSANEMEI || type == ChipType::HSANTIANGONG0 ||
            type == ChipType::HSANTIANGONG1 || type == ChipType::HSANTIANGONG2);
}

bool Global::CmdParamWhitelisting(QPair<QString, QString> mapfolder)
{
    QMap<QString, QStringList> whiteListing = {
        {"isErase", QStringList() << "1"},
        {"BAUD", QStringList() << "SWD"},
        {"speed", QStringList() << "1000" << "4000"},
        {"JLinkScriptFile", QStringList() << "ConnectCore1.JLinkScript" <<
                                "eflash0_chip_erase.JLinkScript,eflash1_chip_erase.JLinkScript"},
        {"autoconnect", QStringList() << "1"}
    };
    auto it = whiteListing.begin();
    for (; it != whiteListing.end(); it++) {
        if (mapfolder.first == it.key()) {
            break;
        }
    }
    if (it == whiteListing.end()) {
        return false;
    }
    auto it1 = it.value().begin();
    for (; it1 != it.value().end(); it1++) {
        if (mapfolder.second == *it1) {
            break;
        }
    }

    if (it1 == it.value().end()) {
        return false;
    }
    return true;
}

void Global::CloseApp(unsigned int result)
{
    qApp->exit(result);
}

QString Global::HidEnumerate(QMap <QString, HidDeviceInfo> &mapName)
{
    GUID interfaceClassGuid;
    CONFIGRET cr;
    CHAR* deviceInterfaceList = nullptr;
    DWORD len;
    HidD_GetHidGuid(&interfaceClassGuid);
    QString retStr = "";
    do {
        cr = CM_Get_Device_Interface_List_SizeA(&len, &interfaceClassGuid,
                                                nullptr, CM_GET_DEVICE_INTERFACE_LIST_PRESENT);
        if (cr != CR_SUCCESS) {
            retStr = "Failed to get size of HID device interface list. \n";
            break;
        }

        if (deviceInterfaceList != nullptr) {
            delete [] deviceInterfaceList;
            deviceInterfaceList = nullptr;
        }

        if (len <= 0) {
            printf("Memory is not compliant");
        }
        deviceInterfaceList = new CHAR[len]();
        if (deviceInterfaceList == nullptr) {
            retStr = "Failed to allocate memory for HID device interface list. \n";
            return retStr;
        }
        cr = CM_Get_Device_Interface_ListA(&interfaceClassGuid, nullptr, deviceInterfaceList,
                                           len, CM_GET_DEVICE_INTERFACE_LIST_PRESENT);
        if (cr != CR_SUCCESS && cr != CR_BUFFER_SMALL) {
            retStr = "Failed to get HID device interface list. \n";
        }
    } while (cr == CR_BUFFER_SMALL);

    if (deviceInterfaceList == nullptr) {
        return retStr;
    }
    if (cr != CR_SUCCESS) {
        delete [] deviceInterfaceList;
        deviceInterfaceList = nullptr;
        return retStr;
    }

    HidEnumerateAdd(mapName, deviceInterfaceList);
    delete [] deviceInterfaceList;
    deviceInterfaceList = nullptr;
    return retStr;
}

void Global::HidEnumerateAdd(QMap <QString, HidDeviceInfo> &mapName, CHAR* deviceInterfaceList)
{
    for (CHAR* deviceDlgInterface = deviceInterfaceList; *deviceDlgInterface != 0;
         deviceDlgInterface += strlen(deviceDlgInterface) + 1) {
        HANDLE deviceHandle = INVALID_HANDLE_VALUE;
        HidDeviceInfo hidDeviceInfo;
        /* Open read-only handle to the device */
        deviceHandle = OpenDevice(deviceDlgInterface, FALSE);
        /* Check validity of device_handle. */
        if (deviceHandle == INVALID_HANDLE_VALUE) {
            /* Unable to open the device. */
            continue;
        }
        wchar_t string[LEN] = {0};
        QString productString = "";
        if (HidD_GetProductString(deviceHandle, string, LEN)) {
            string[LEN - 1] = 0;
            productString = QString::fromWCharArray(string, wcslen(string));
            hidDeviceInfo.productName = productString;
        }
        HIDD_ATTRIBUTES attrib;

        if (HidD_GetAttributes(deviceHandle, &attrib)) {
            /* VID/PID */
            hidDeviceInfo.vid = attrib.VendorID;
            hidDeviceInfo.pid = attrib.ProductID;
        }
        PHIDP_PREPARSED_DATA data = nullptr;
        HIDP_CAPS caps;
        if (HidD_GetPreparsedData(deviceHandle, &data)) {
            if (HidP_GetCaps(data, &caps) == HIDP_STATUS_SUCCESS) {
                hidDeviceInfo.usagePage = caps.UsagePage;
                hidDeviceInfo.usage = caps.Usage;
            }
            mapName[QString::fromUtf8(deviceDlgInterface)] = hidDeviceInfo;
            HidD_FreePreparsedData(data);
        }
        CloseHandle(deviceHandle);
    }
}

HANDLE Global::OpenDevice(const CHAR *path, BOOL openRw)
{
    DWORD desiredAccess = (openRw) ? (GENERIC_WRITE | GENERIC_READ) : 0;
    DWORD shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
    HANDLE handle = CreateFileA(path, desiredAccess, shareMode, nullptr,
                                OPEN_EXISTING, FILE_FLAG_OVERLAPPED, nullptr);
    return handle;
}

int Global::Parity2Int(QSerialPort::Parity par)
{
    if (par == QSerialPort::NoParity) {
        return static_cast<int>(InterfaceParity::INTERFACE_PARITY_NO);
    } else if (par == QSerialPort::OddParity) {
        return static_cast<int>(InterfaceParity::INTERFACE_PARITY_ODD);
    } else if (par == QSerialPort::EvenParity) {
        return static_cast<int>(InterfaceParity::INTERFACE_PARITY_EVEN);
    } else if (par == QSerialPort::MarkParity) {
        return static_cast<int>(InterfaceParity::INTERFACE_PARITY_MARK);
    } else if (par == QSerialPort::SpaceParity) {
        return static_cast<int>(InterfaceParity::INTERFACE_PARITY_SPACE);
    } else {
        return static_cast<int>(InterfaceParity::INTERFACE_PARITY_NO);
    }
}

int Global::Stopbits2Int(int stopBits)
{
    if (stopBits == static_cast<int>(STOP_BITS_NUM::STOPBITS_VALUE_IS_ONE)) {
        return 0;
    } else if (stopBits == static_cast<int>(STOP_BITS_NUM::STOPBITS_VALUE_IS_TWO)) {
        return 1;
    } else if (stopBits == static_cast<int>(STOP_BITS_NUM::STOPBITS_VALUE_IS_THREE)) {
        return RET_TWO;
    } else {
        return 0;
    }
}

QByteArray Global::QString2Gbk(QString str)
{
    QTextCodec *gbk = QTextCodec::codecForName("gbk");
    QByteArray arr = gbk->fromUnicode(str);
    return arr;
}

bool Global::IsWs63(ChipType type)
{
    if (type == ChipType::WS63 || type == ChipType::TCPWS63) {
        return true;
    }
    return false;
}
